// D3D10Scene.h

#pragma once
#include <windows.h>

// C RunTime Header Files
#include <stdlib.h>
#include <stdio.h>

#include "d3d9.h"
#include "d3dx9.h"
#include "D3D10_1.h"
#include "d3dx10.h"

using namespace System;
#define TEXTURE_PATH ".\\DirectX.bmp"
#define EFFECT_PATH ".\\TextureMap.fx"

struct SimpleVertex
{
    D3DXVECTOR3 Pos;
    D3DXVECTOR2 Tex;
};

SimpleVertex VERTEX_DATA[] =
{
    { D3DXVECTOR3(  0.5f,   0.5f, 0), D3DXVECTOR2(1.0f, 0.0f) },
    { D3DXVECTOR3(  0.5f,  -0.5f, 0), D3DXVECTOR2(1.0f, 1.0f) },
    { D3DXVECTOR3( -0.5f,  -0.5f, 0), D3DXVECTOR2(0.0f, 1.0f) },
	{ D3DXVECTOR3( -0.5f,   0.5f, 0), D3DXVECTOR2(0.0f, 0.0f) },
};

USHORT INDEX_DATA[] = 
{
    0, 1, 2, 0, 2, 3
};

float ClearColor[4] = { 0, 0, 0, 0};

namespace D3D10Scene {

	public ref class D3D10TestScene
	{
	public:
		D3D10TestScene()
		{
			m_pTexture2D10 = NULL;
			WIDTH = 640;
			HEIGHT = 480;
			count = 0;
			m_pRenderTargetView = NULL;

			InitD3D10();
			SetViews();
		}

		IntPtr GetRenderTarget()
		{
			return IntPtr(m_pTexture2D10);
		}

		void Render()
		{
			ID3D10RenderTargetView*		pRenderTargetView;
			HRESULT hr = S_OK;

    	    hr = g_D3D10Device->CreateRenderTargetView(m_pTexture2D10, NULL, &pRenderTargetView);
            g_D3D10Device->OMSetRenderTargets(1, &pRenderTargetView, NULL);
			m_pRenderTargetView = pRenderTargetView; 

			g_D3D10Device->ClearRenderTargetView(m_pRenderTargetView, ClearColor);
			RenderD3D10(count);

			g_D3D10Device->OMSetRenderTargets(0, 0, 0);
			m_pRenderTargetView->Release();

			g_D3D10Device->Flush();
			count+=10;
		}

	private:
		ID3D10Device1*              g_D3D10Device;
		ID3D10EffectTechnique*      g_D3D10Technique;
		ID3D10EffectMatrixVariable* g_D3D10Matrix;
		ID3D10Texture2D*			m_pTexture2D10;
		ID3D10RenderTargetView*		m_pRenderTargetView;  

		UINT WIDTH;
		UINT HEIGHT;
		int count;
	private:
		void RenderD3D10(int count)
		{
			float angle = (count / 200.0f);
			D3DXMATRIX matrix;
			D3DXMatrixIdentity(&matrix);
			D3DXMatrixRotationY(&matrix, angle);
			g_D3D10Matrix->SetMatrix( (float*) &matrix);

			D3D10_TECHNIQUE_DESC techDesc;
			HRESULT hr = g_D3D10Technique->GetDesc(&techDesc);
			for (UINT p = 0; p < techDesc.Passes; ++p)
			{
				hr = g_D3D10Technique->GetPassByIndex(p)->Apply( 0 );
				g_D3D10Device->DrawIndexed(ARRAYSIZE(INDEX_DATA), 0, 0);
			}
		}

		HRESULT SetViews()
		{
			ID3D10Texture2D* pTexture = NULL;

			D3D10_TEXTURE2D_DESC Desc;
			Desc.Width              = WIDTH;
			Desc.Height             = HEIGHT;
			Desc.MipLevels          = 1;
			Desc.ArraySize          = 1;
			Desc.Format             = DXGI_FORMAT_B8G8R8A8_UNORM;
			Desc.SampleDesc.Count   = 1;
			Desc.SampleDesc.Quality = 0;
			Desc.Usage              = D3D10_USAGE_DEFAULT;
			Desc.BindFlags          = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
			Desc.CPUAccessFlags     = 0;
			Desc.MiscFlags          = D3D10_RESOURCE_MISC_SHARED;

			HRESULT hr = g_D3D10Device->CreateTexture2D(&Desc, NULL, &pTexture);

			m_pTexture2D10 = pTexture;

			return S_OK;
		}

		HRESULT CreateGeometry(ID3D10Device* pDevice, ID3D10Buffer** ppVertexBuffer, ID3D10Buffer** ppIndexBuffer)
		{
			HRESULT hr = S_OK;

			D3D10_BUFFER_DESC bd;
			bd.Usage            = D3D10_USAGE_DEFAULT;
			bd.ByteWidth        = sizeof(VERTEX_DATA);
			bd.BindFlags        = D3D10_BIND_VERTEX_BUFFER;
			bd.CPUAccessFlags   = 0;
			bd.MiscFlags        = 0;
			D3D10_SUBRESOURCE_DATA InitData;
			InitData.pSysMem	= VERTEX_DATA;

			if ( FAILED (hr = pDevice->CreateBuffer(&bd, &InitData, ppVertexBuffer) ) )
			{
				return hr;
			}
			UINT stride = sizeof(SimpleVertex);
			UINT offset = 0;
			pDevice->IASetVertexBuffers(0, 1, ppVertexBuffer, &stride, &offset);

			bd.Usage			= D3D10_USAGE_DEFAULT;
			bd.ByteWidth		= sizeof(INDEX_DATA);
			bd.BindFlags		= D3D10_BIND_INDEX_BUFFER;
			bd.CPUAccessFlags	= 0;
			bd.MiscFlags		= 0;
			InitData.pSysMem	= INDEX_DATA;
			if (FAILED(hr = pDevice->CreateBuffer(&bd, &InitData, ppIndexBuffer)))
			{
				return hr;
			}
			pDevice->IASetIndexBuffer(*ppIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
			pDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

			return hr;
		}

		HRESULT InitD3D10()
		{
			HRESULT hr;
			ID3D10Effect*               pEffect;
			ID3D10InputLayout*          pVertexLayout;
			ID3D10Buffer*               pVertexBuffer;
			ID3D10Buffer*               pIndexBuffer;
		    ID3D10Device1*              pD3D10Device;

			UINT		DeviceFlags = 0;
			DWORD		dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;

		#ifdef _DEBUG
			DeviceFlags		|= D3D10_CREATE_DEVICE_DEBUG;
			dwShaderFlags	|= D3D10_SHADER_DEBUG;
		#endif

			if (FAILED(hr = D3D10CreateDevice1(NULL, D3D10_DRIVER_TYPE_HARDWARE, NULL,
							DeviceFlags, D3D10_FEATURE_LEVEL_10_0, D3D10_1_SDK_VERSION, &pD3D10Device)))
			{
				return hr;
			}

			g_D3D10Device = pD3D10Device;

			D3D10_VIEWPORT vp;
			vp.Width = WIDTH;
			vp.Height = HEIGHT;
			vp.MinDepth = 0.0f;
			vp.MaxDepth = 1.0f;
			vp.TopLeftX = 0;
			vp.TopLeftY = 0;
			g_D3D10Device->RSSetViewports( 1, &vp );

			// Create the effect
			ID3D10ShaderResourceView*           pTextureRV;
			ID3D10EffectShaderResourceVariable* pTextureVariable;
			if (FAILED(hr = D3DX10CreateShaderResourceViewFromFile(g_D3D10Device, TEXTURE_PATH, NULL, NULL, &pTextureRV, NULL)))
			{
				return hr;
			}
			if (FAILED (hr = D3DX10CreateEffectFromFile( EFFECT_PATH, NULL, NULL, "fx_4_0", dwShaderFlags, 0, 
							g_D3D10Device, NULL, NULL, &pEffect, NULL, NULL)))
			{
				return hr;
			}
			g_D3D10Technique = pEffect->GetTechniqueByName( "Render" );
			pTextureVariable = pEffect->GetVariableByName( "txDiffuse" ) -> AsShaderResource();

			D3D10_INPUT_ELEMENT_DESC layout[] =
			{
				{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
				{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
			};
			UINT numElements = sizeof(layout) / sizeof(layout[0]);

			D3D10_PASS_DESC PassDesc;
			g_D3D10Technique->GetPassByIndex(0)->GetDesc(&PassDesc);
			if (FAILED (hr = g_D3D10Device->CreateInputLayout( layout, numElements, PassDesc.pIAInputSignature,
							PassDesc.IAInputSignatureSize, &pVertexLayout) ) )
			{
				return hr;
			}
			g_D3D10Device->IASetInputLayout(pVertexLayout);
			pTextureVariable->SetResource(pTextureRV);

			g_D3D10Matrix = pEffect->GetVariableByName("WorldMatrix")->AsMatrix();

			/* Create the geometry */
			if (FAILED(hr = CreateGeometry(g_D3D10Device, &pVertexBuffer, &pIndexBuffer)))
			{
				return hr;
			}

			D3D10_RASTERIZER_DESC rasterizerState;
			ZeroMemory(&rasterizerState, sizeof(D3D10_RASTERIZER_DESC));
			rasterizerState.CullMode				= D3D10_CULL_NONE;
			rasterizerState.FillMode				= D3D10_FILL_SOLID;


			ID3D10RasterizerState* pRS;
			g_D3D10Device->CreateRasterizerState( &rasterizerState, &pRS);
			g_D3D10Device->RSSetState(pRS);

			return S_OK;
		}
	};
}
